#!/usr/bin/python
#
# This file is part of OpenRTI.
#
# OpenRTI is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# OpenRTI is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with OpenRTI.  If not, see <http://www.gnu.org/licenses/>.
#

class SourceStream(object):
    def __init__(self, stream):
        self.__stream = stream
        self.__indent = 0

    def pushIndent(self):
        self.__indent += 1

    def popIndent(self):
        self.__indent -= 1

    def write(self, data):
        self.__stream.write(data)

    def writeline(self, line = None):
        if line is not None:
            count = self.__indent
            while 0 < count:
                self.write('  ')
                count = count - 1
            self.write(line)
        self.write('\n')

    def writelineNoIndent(self, line = None):
        self.write(line)
        self.write('\n')

    def writeCopyright(self):
        self.writeline('/* -*-c++-*- OpenRTI - Copyright (C) 2009-2023 Mathias Froehlich')
        self.writeline(' *')
        self.writeline(' *')
        self.writeline(' * This file is part of OpenRTI.')
        self.writeline(' *')
        self.writeline(' * OpenRTI is free software: you can redistribute it and/or modify')
        self.writeline(' * it under the terms of the GNU Lesser General Public License as published by')
        self.writeline(' * the Free Software Foundation, either version 2.1 of the License, or')
        self.writeline(' * (at your option) any later version.')
        self.writeline(' *')
        self.writeline(' * OpenRTI is distributed in the hope that it will be useful,')
        self.writeline(' * but WITHOUT ANY WARRANTY; without even the implied warranty of')
        self.writeline(' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the')
        self.writeline(' * GNU Lesser General Public License for more details.')
        self.writeline(' *')
        self.writeline(' * You should have received a copy of the GNU Lesser General Public License')
        self.writeline(' * along with OpenRTI.  If not, see <http://www.gnu.org/licenses/>.')
        self.writeline(' *')
        self.writeline(' * This file is autogenerated by messagegen.py. Do not edit!')
        self.writeline(' *')
        self.writeline(' */')
        self.writeline()


###############################################################################
class DataType(object):
    def __init__(self, name, hasSwap):
        self.__name = name
        self.__hasSwap = hasSwap
        self._hasPayload = None

    def getName(self):
        return self.__name

    def isMessage(self):
        return False

    def resolveHasPayloadAndHasSwap(self, typeMap):
        pass

    def hasPayload(self):
        return self._hasPayload

    def hasSwap(self):
        return self.__hasSwap

    def writeForwardDeclaration(self, sourceStream):
        pass

    def writeDeclaration(self, sourceStream):
        pass

    def writeImplementation(self, sourceStream):
        pass

    def writeComponent(self, component, sourceStream, messageEncoding):
        pass

    def writeStreamOut(self, sourceStream):
        pass


###############################################################################
class CDataType(DataType):
    def __init__(self, name, encoding, ctype):
        if ctype == 'VariableLengthData' or ctype == 'std::string':
            hasSwap = True
        else:
            hasSwap = False
        DataType.__init__(self, name, hasSwap)
        self.__encoding = encoding
        self.__ctype = ctype

    def getEncoding(self):
        return self.__encoding

    def getTypeName(self):
        return self.__ctype

    def resolveHasPayloadAndHasSwap(self, typeMap):
        self._hasPayload = self.getTypeName() == 'VariableLengthData'

    def writeForwardDeclaration(self, sourceStream):
        self.writeDeclaration(sourceStream)

    def writeDeclaration(self, sourceStream):
        name = self.getName()
        typeName = self.getTypeName()
        if typeName == name:
            return
        sourceStream.writeline('typedef {typeName} {name};'.format(typeName = typeName, name = name))
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writeC' + component)(self, sourceStream)


###############################################################################
class EnumLabel(object):
    def __init__(self, name, value):
        self.__name = name
        self.__value = value

    def getName(self):
        return self.__name

    def getValue(self):
        return self.__value

class EnumDataType(DataType):
    def __init__(self, name):
        DataType.__init__(self, name, False)
        self.__enumList = []

    def addEnum(self, name, value):
        self.__enumList.append(EnumLabel(name, value))

    def getEnumList(self):
        return self.__enumList

    def writeForwardDeclaration(self, sourceStream):
        # enums cannot be forward declared, so, emit the full definition
        sourceStream.writeline('enum {name} {{'.format(name = self.getName()))
        sourceStream.pushIndent()

        index = 0
        for enum in self.__enumList:
            line = '{enum}'.format(enum = enum.getName())
            value = enum.getValue()
            if value is not None:
                line += ' = {value}'.format(value = value)
            index = index + 1
            if index < len(self.__enumList):
                line += ','
            sourceStream.writeline(line)

        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writeEnum' + component)(self, sourceStream)

    def writeStreamOut(self, sourceStream):
        sourceStream.writeline('template<typename char_type, typename traits_type>')
        sourceStream.writeline('std::basic_ostream<char_type, traits_type>&')
        sourceStream.writeline('operator<<(std::basic_ostream<char_type, traits_type>& os, const {name}& value)'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  switch (value) {')
        for enum in self.__enumList:
            sourceStream.writeline('  case {enum}: os << "{enum}"; break;'.format(enum = enum.getName()))
        sourceStream.writeline('  }')
        sourceStream.writeline('  return os;')
        sourceStream.writeline('}')
        sourceStream.writeline()


###############################################################################
class VectorDataType(DataType):
    def __init__(self, name, scalarTypeName):
        DataType.__init__(self, name, True)
        self.__scalarTypeName = scalarTypeName

    def getScalarTypeName(self):
        return self.__scalarTypeName

    def resolveHasPayloadAndHasSwap(self, typeMap):
        t = typeMap.getType(self.getScalarTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self._hasPayload = t.hasPayload()

    def writeForwardDeclaration(self, sourceStream):
        self.writeDeclaration(sourceStream)

    def writeDeclaration(self, sourceStream):
        scalar = self.__scalarTypeName
        sourceStream.writeline('typedef std::vector<{scalar}> {name};'.format(scalar = scalar, name = self.getName()))
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writeVector' + component)(self, sourceStream)

    def writeStreamOut(self, sourceStream):
        # FIXME, make the std::* containers with a template
        name = self.getName();
        sourceStream.writeline('template<typename char_type, typename traits_type>')
        sourceStream.writeline('std::basic_ostream<char_type, traits_type>&')
        sourceStream.writeline('operator<<(std::basic_ostream<char_type, traits_type>& os, const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  os << "{ ";')
        sourceStream.writeline('  {name}::const_iterator i = value.begin();'.format(name = name))
        sourceStream.writeline('  if (i != value.end()) {')
        sourceStream.writeline('    os << *i;')
        sourceStream.writeline('    while (++i != value.end()) {')
        sourceStream.writeline('      os << ", " << *i;')
        sourceStream.writeline('    }')
        sourceStream.writeline('  }')
        sourceStream.writeline('  os << " }";')
        sourceStream.writeline('  return os;')
        sourceStream.writeline('}')
        sourceStream.writeline()


###############################################################################
class SetDataType(DataType):
    def __init__(self, name, scalarTypeName):
        DataType.__init__(self, name, True)
        self.__scalarTypeName = scalarTypeName

    def getScalarTypeName(self):
        return self.__scalarTypeName

    def resolveHasPayloadAndHasSwap(self, typeMap):
        t = typeMap.getType(self.getScalarTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self._hasPayload = t.hasPayload()

    def writeForwardDeclaration(self, sourceStream):
        self.writeDeclaration(sourceStream)

    def writeDeclaration(self, sourceStream):
        scalar = self.__scalarTypeName
        sourceStream.writeline('typedef std::set<{scalar}> {name};'.format(scalar = scalar, name = self.getName()))
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writeSet' + component)(self, sourceStream)

    def writeStreamOut(self, sourceStream):
        # FIXME, make the std::* containers with a template
        name = self.getName();
        sourceStream.writeline('template<typename char_type, typename traits_type>')
        sourceStream.writeline('std::basic_ostream<char_type, traits_type>&')
        sourceStream.writeline('operator<<(std::basic_ostream<char_type, traits_type>& os, const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  os << "{ ";')
        sourceStream.writeline('  {name}::const_iterator i = value.begin();'.format(name = name))
        sourceStream.writeline('  if (i != value.end()) {')
        sourceStream.writeline('    os << *i;')
        sourceStream.writeline('    while (++i != value.end()) {')
        sourceStream.writeline('      os << ", " << *i;')
        sourceStream.writeline('    }')
        sourceStream.writeline('  }')
        sourceStream.writeline('  os << " }";')
        sourceStream.writeline('  return os;')
        sourceStream.writeline('}')
        sourceStream.writeline()


###############################################################################
class MapDataType(DataType):
    def __init__(self, name, keyTypeName, valueTypeName):
        DataType.__init__(self, name, True)
        self.__keyTypeName = keyTypeName
        self.__valueTypeName = valueTypeName
        self.__keyHasPayload = None
        self.__valueHasPayload = None

    def getKeyTypeName(self):
        return self.__keyTypeName

    def getValueTypeName(self):
        return self.__valueTypeName

    def resolveHasPayloadAndHasSwap(self, typeMap):
        t = typeMap.getType(self.getKeyTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self.__keyHasPayload = t.hasPayload()
        t = typeMap.getType(self.getValueTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self.__valueHasPayload = t.hasPayload()
        self._hasPayload = self.__keyHasPayload or self.__valueHasPayload

    def keyHasPayload(self):
        return self.__keyHasPayload

    def valueHasPayload(self):
        return self.__valueHasPayload

    def writeForwardDeclaration(self, sourceStream):
        self.writeDeclaration(sourceStream)

    def writeDeclaration(self, sourceStream):
        keyTypeName = self.__keyTypeName
        valueTypeName = self.__valueTypeName
        sourceStream.writeline('typedef std::map<{keyTypeName}, {valueTypeName}> {name};'.format(keyTypeName = keyTypeName, valueTypeName = valueTypeName, name = self.getName()))
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writeMap' + component)(self, sourceStream)

    def writeStreamOut(self, sourceStream):
        # FIXME, make the std::* containers with a template
        name = self.getName();
        sourceStream.writeline('template<typename char_type, typename traits_type>')
        sourceStream.writeline('std::basic_ostream<char_type, traits_type>&')
        sourceStream.writeline('operator<<(std::basic_ostream<char_type, traits_type>& os, const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  os << "{ ";')
        sourceStream.writeline('  {name}::const_iterator i = value.begin();'.format(name = name))
        sourceStream.writeline('  if (i != value.end()) {')
        sourceStream.writeline('    os << i->first << ": " << i->second;')
        sourceStream.writeline('    while (++i != value.end()) {')
        sourceStream.writeline('      os << ", " << i->first << ": " << i->second;')
        sourceStream.writeline('    }')
        sourceStream.writeline('  }')
        sourceStream.writeline('  os << " }";')
        sourceStream.writeline('  return os;')
        sourceStream.writeline('}')
        sourceStream.writeline()


###############################################################################
class PairDataType(DataType):
    def __init__(self, name, firstTypeName, secondTypeName):
        DataType.__init__(self, name, False)
        self.__firstTypeName = firstTypeName
        self.__secondTypeName = secondTypeName
        self.__firstHasPayload = None
        self.__secondHasPayload = None

    def getFirstTypeName(self):
        return self.__firstTypeName

    def getSecondTypeName(self):
        return self.__secondTypeName

    def resolveHasPayloadAndHasSwap(self, typeMap):
        t = typeMap.getType(self.getFirstTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self.__firstHasPayload = t.hasPayload()
        t = typeMap.getType(self.getSecondTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self.__secondHasPayload = t.hasPayload()
        self._hasPayload = self.__firstHasPayload or self.__secondHasPayload

    def firstHasPayload(self):
        return self.__firstHasPayload

    def secondHasPayload(self):
        return self.__secondHasPayload

    def writeForwardDeclaration(self, sourceStream):
        self.writeDeclaration(sourceStream)

    def writeDeclaration(self, sourceStream):
        name = self.getName()
        firstTypeName = self.__firstTypeName
        secondTypeName = self.__secondTypeName
        sourceStream.writeline('typedef std::pair<{first}, {second}> {name};'.format(first = firstTypeName, second = secondTypeName, name = name))
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writePair' + component)(self, sourceStream)

    def writeStreamOut(self, sourceStream):
        sourceStream.writeline('template<typename char_type, typename traits_type>')
        sourceStream.writeline('std::basic_ostream<char_type, traits_type>&')
        sourceStream.writeline('operator<<(std::basic_ostream<char_type, traits_type>& os, const {name}& value)'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  os << "{ ";')
        sourceStream.writeline('  os << "first: " << value.first << ", ";')
        sourceStream.writeline('  os << "second: " << value.second;')
        sourceStream.writeline('  os << " }";')
        sourceStream.writeline('  return os;')
        sourceStream.writeline('}')
        sourceStream.writeline()


###############################################################################
class StructField(object):
    def __init__(self, name, typeName):
        self.__name = name
        self.__typeName = typeName
        self.__hasSwap = False
        self._hasPayload = None

    def getName(self):
        return self.__name

    def getLowerName(self):
        return self.__name[0].lower() + self.__name[1:len(self.__name)]

    def getUpperName(self):
        return self.__name[0].upper() + self.__name[1:len(self.__name)]

    def getMemberName(self):
        return '_' + self.getLowerName()

    def getTypeName(self):
        return self.__typeName

    def resolveHasPayloadAndHasSwap(self, typeMap):
        t = typeMap.getType(self.getTypeName())
        t.resolveHasPayloadAndHasSwap(typeMap)
        self._hasPayload = t.hasPayload()

        self.__hasSwap = t.hasSwap()

    def hasPayload(self):
        return self._hasPayload

    def writeSetter(self, sourceStream, valuePrefix):
        upperName = self.getUpperName()
        typeName = self.getTypeName()
        memberName = self.getMemberName()
        sourceStream.writeline('void set{upperName}(const {typeName}& value)'.format(upperName = upperName, typeName = typeName))
        sourceStream.writeline('{{ {prefix}{memberName} = value; }}'.format(memberName = memberName, prefix = valuePrefix))
        sourceStream.writelineNoIndent('#if 201103L <= __cplusplus || 200610L <= __cpp_rvalue_reference')
        sourceStream.writeline('void set{upperName}({typeName}&& value)'.format(upperName = upperName, typeName = typeName))
        sourceStream.writeline('{{ {prefix}{memberName} = std::move(value); }}'.format(memberName = memberName, prefix = valuePrefix))
        sourceStream.writelineNoIndent('#endif')

    def writeGetter(self, sourceStream, valuePrefix):
        upperName = self.getUpperName()
        typeName = self.getTypeName()
        memberName = self.getMemberName()
        sourceStream.writeline('{typeName}& get{upperName}()'.format(upperName = upperName, typeName = typeName))
        sourceStream.writeline('{{ return {prefix}{memberName}; }}'.format(memberName = memberName, prefix = valuePrefix))

    def writeConstGetter(self, sourceStream, valuePrefix):
        upperName = self.getUpperName()
        typeName = self.getTypeName()
        memberName = self.getMemberName()
        sourceStream.writeline('const {typeName}& get{upperName}() const'.format(upperName = upperName, typeName = typeName))
        sourceStream.writeline('{{ return {prefix}{memberName}; }}'.format(memberName = memberName, prefix = valuePrefix))

    def writeMemberInstance(self, sourceStream):
        typeName = self.getTypeName()
        memberName = self.getMemberName()
        sourceStream.writeline('{typeName} {memberName};'.format(typeName = typeName, memberName = memberName))

    def writeSwap(self, sourceStream, value):
        memberName = self.getMemberName()
        if self.__hasSwap:
            sourceStream.writeline('{memberName}.swap({value}.{memberName});'.format(memberName = memberName, value = value))
        else:
            sourceStream.writeline('std::swap({memberName}, {value}.{memberName});'.format(memberName = memberName, value = value))

###############################################################################
class StructDataType(DataType):
    def __init__(self, name, parentTypeName = None):
        DataType.__init__(self, name, True)
        self.__parentTypeName = parentTypeName
        self.__fieldList = []
        self.__cow = False

    def setCopyOnWrite(self, cow):
        self.__cow = cow

    def getCopyOnWrite(self):
        return self.__cow

    def addField(self, name, typeName):
        self.__fieldList.append(StructField(name, typeName))

    def getFieldList(self):
        return self.__fieldList

    def getParentTypeName(self):
        return self.__parentTypeName

    def resolveHasPayloadAndHasSwap(self, typeMap):
        for field in self.__fieldList:
            field.resolveHasPayloadAndHasSwap(typeMap)
        for field in self.__fieldList:
            self._hasPayload = self.hasPayload() or field.hasPayload()

    def writeForwardDeclaration(self, sourceStream):
        sourceStream.writeline('class {name};'.format(name = self.getName()))

    def writeDeclaration(self, sourceStream):
        if self.getParentTypeName() is None:
            sourceStream.writeline('class OPENRTI_API {name} {{'.format(name = self.getName()))
        else:
            sourceStream.writeline('class OPENRTI_API {name} : public {parentTypeName} {{'.format(name = self.getName(), parentTypeName = self.getParentTypeName()))
        sourceStream.writeline('public:')
        sourceStream.pushIndent()

        if self.__cow:
            sourceStream.writeline('{name}() : '.format(name = self.getName()))
            sourceStream.writeline('  _impl(new Implementation())')
            sourceStream.writeline('{ }')
            valuePrefix = 'getImpl().'
            constValuePrefix = 'getConstImpl().'
        else:
            fieldCount = len(self.getFieldList())
            if fieldCount == 0:
                sourceStream.writeline('{name}()'.format(name = self.getName()))
            else:
                sourceStream.writeline('{name}() :'.format(name = self.getName()))
            sourceStream.pushIndent()
            index = 0
            for field in self.getFieldList():
                line = '{memberName}()'.format(memberName = field.getMemberName())
                index = index + 1
                if index < fieldCount:
                    line += ','
                sourceStream.writeline(line)
            sourceStream.popIndent()
            sourceStream.writeline('{ }')
            valuePrefix = ''
            constValuePrefix = ''

        for field in self.__fieldList:
            field.writeSetter(sourceStream, valuePrefix)
            field.writeGetter(sourceStream, valuePrefix)
            field.writeConstGetter(sourceStream, constValuePrefix)
            sourceStream.writeline()

        sourceStream.writeline('{name}& swap({name}& rhs)'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.pushIndent()
        if self.__cow:
            sourceStream.writeline('_impl.swap(rhs._impl);')
        else:
            for field in self.__fieldList:
                field.writeSwap(sourceStream, 'rhs')
        sourceStream.writeline('return *this;')
        sourceStream.popIndent()
        sourceStream.writeline('}')

        sourceStream.writeline('bool operator==(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        if self.__cow:
            sourceStream.writeline('  if (_impl.get() == rhs._impl.get())')
            sourceStream.writeline('    return true;')
        for field in self.getFieldList():
            upperName = field.getUpperName()
            sourceStream.writeline('  if (get{upperName}() != rhs.get{upperName}()) return false;'.format(upperName = upperName))
        sourceStream.writeline('  return true;')
        sourceStream.writeline('}')
        sourceStream.writeline('bool operator<(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        if self.__cow:
            sourceStream.writeline('  if (_impl.get() == rhs._impl.get())')
            sourceStream.writeline('    return false;')
        for field in self.getFieldList():
            upperName = field.getUpperName()
            sourceStream.writeline('  if (get{upperName}() < rhs.get{upperName}()) return true;'.format(upperName = upperName))
            sourceStream.writeline('  if (rhs.get{upperName}() < get{upperName}()) return false;'.format(upperName = upperName))
        sourceStream.writeline('  return false;')
        sourceStream.writeline('}')
        sourceStream.writeline('bool operator!=(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return !operator==(rhs); }')
        sourceStream.writeline('bool operator>(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return rhs.operator<(*this); }')
        sourceStream.writeline('bool operator>=(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return !operator<(rhs); }')
        sourceStream.writeline('bool operator<=(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return !operator>(rhs); }')

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()

        if self.__cow:
            sourceStream.writeline('struct OPENRTI_API Implementation : public Referenced {')
            sourceStream.pushIndent()

            fieldCount = len(self.getFieldList())
            if fieldCount == 0:
                sourceStream.writeline('Implementation()'.format(name = self.getName()))
            else:
                sourceStream.writeline('Implementation() :'.format(name = self.getName()))
            sourceStream.pushIndent()
            index = 0
            for field in self.getFieldList():
                line = '{memberName}()'.format(memberName = field.getMemberName())
                index = index + 1
                if index < fieldCount:
                    line += ','
                sourceStream.writeline(line)
            sourceStream.popIndent()
            sourceStream.writeline('{ }')
 
            
        for field in self.__fieldList:
            field.writeMemberInstance(sourceStream)

        if self.__cow:
            sourceStream.popIndent()
            sourceStream.writeline('};')
            sourceStream.writeline()
            sourceStream.writeline('const Implementation& getConstImpl() const')
            sourceStream.writeline('{')
            sourceStream.writeline('  return *_impl;')
            sourceStream.writeline('}')
            sourceStream.writeline()
            sourceStream.writeline('Implementation& getImpl()')
            sourceStream.writeline('{')
            sourceStream.writeline('  if (1 < Referenced::count(_impl.get()))')
            sourceStream.writeline('    _impl = new Implementation(*_impl);')
            sourceStream.writeline('  return *_impl;')
            sourceStream.writeline('}')
            sourceStream.writeline()
            sourceStream.writeline('SharedPtr<Implementation> _impl;')

        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

    def writeComponent(self, component, sourceStream, messageEncoding):
        getattr(messageEncoding, 'writeStruct' + component)(self, sourceStream)

    def writeStreamOut(self, sourceStream):
        sourceStream.writeline('template<typename char_type, typename traits_type>')
        sourceStream.writeline('std::basic_ostream<char_type, traits_type>&')
        sourceStream.writeline('operator<<(std::basic_ostream<char_type, traits_type>& os, const {name}& value)'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  os << "{ ";')
        count = 0
        for field in self.__fieldList:
            count = count + 1
            lowerName = field.getLowerName()
            upperName = field.getUpperName()
            sourceStream.writeline('  os << "{lowerName}: " << value.get{upperName}();'.format(lowerName = lowerName, upperName = upperName))
            if count != len(self.__fieldList):
                sourceStream.writeline('  os << ", ";')
        sourceStream.writeline('  os << " }";')
        sourceStream.writeline('  return os;')
        sourceStream.writeline('}')
        sourceStream.writeline()


###############################################################################
class MessageDataType(StructDataType):
    def __init__(self, name, parentTypeName = None):
        StructDataType.__init__(self, name, parentTypeName)
        self.__reliableExpression = None
        self.__objectInstanceExpression = None
         
    def isMessage(self):
        return True

    def setReliableExpression(self, reliableExpression):
        self.__reliableExpression = reliableExpression

    def getReliableExpression(self):
        return self.__reliableExpression

    def setObjectInstanceExpression(self, objectInstanceExpression):
        self.__objectInstanceExpression = objectInstanceExpression

    def getObjectInstanceExpression(self):
        return self.__objectInstanceExpression

    def writeForwardDeclaration(self, sourceStream):
        sourceStream.writeline('class {name};'.format(name = self.getName()))

    def writeDeclaration(self, sourceStream):
        if self.getParentTypeName() is None:
            sourceStream.writeline('class OPENRTI_API {name} {{'.format(name = self.getName()))
        else:
            sourceStream.writeline('class OPENRTI_API {name} : public {parentTypeName} {{'.format(name = self.getName(), parentTypeName = self.getParentTypeName()))
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('{name}();'.format(name = self.getName()))
        sourceStream.writeline('virtual ~{name}();'.format(name = self.getName()))

        sourceStream.writeline()
        sourceStream.writeline('virtual const char* getTypeName() const;')
        sourceStream.writeline('virtual void out(std::ostream& os) const;')
        sourceStream.writeline('virtual void dispatch(const AbstractMessageDispatcher& dispatcher) const;')
        sourceStream.writeline()

        sourceStream.writeline('virtual bool operator==(const AbstractMessage& rhs) const;')
        sourceStream.writeline('bool operator==(const {name}& rhs) const;'.format(name = self.getName()))
        sourceStream.writeline('bool operator<(const {name}& rhs) const;'.format(name = self.getName()))
        sourceStream.writeline('bool operator!=(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return !operator==(rhs); }')
        sourceStream.writeline('bool operator>(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return rhs.operator<(*this); }')
        sourceStream.writeline('bool operator>=(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return !operator<(rhs); }')
        sourceStream.writeline('bool operator<=(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{ return !operator>(rhs); }')
        sourceStream.writeline()

        if self.getReliableExpression():
            sourceStream.writeline('virtual bool getReliable() const;')
            sourceStream.writeline()

        if self.getObjectInstanceExpression():
            sourceStream.writeline('virtual ObjectInstanceHandle getObjectInstanceHandleForMessage() const;')
            sourceStream.writeline()

        for field in self.getFieldList():
            field.writeSetter(sourceStream, '')
            field.writeGetter(sourceStream, '')
            field.writeConstGetter(sourceStream, '')
            sourceStream.writeline()

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()

        for field in self.getFieldList():
            field.writeMemberInstance(sourceStream)

        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

    def writeImplementation(self, sourceStream):
        fieldCount = len(self.getFieldList())
        if fieldCount == 0:
            sourceStream.writeline('{name}::{name}()'.format(name = self.getName()))
        else:
            sourceStream.writeline('{name}::{name}() :'.format(name = self.getName()))
        sourceStream.pushIndent()
        index = 0
        for field in self.getFieldList():
            line = '{memberName}()'.format(memberName = field.getMemberName())
            index = index + 1
            if index < fieldCount:
                line += ','
            sourceStream.writeline(line)
        sourceStream.popIndent()
        sourceStream.writeline('{')
        sourceStream.writeline('}')
        sourceStream.writeline()
        sourceStream.writeline('{name}::~{name}()'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('}')
        sourceStream.writeline()
        sourceStream.writeline('const char*')
        sourceStream.writeline('{name}::getTypeName() const'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  return "{name}";'.format(name = self.getName()))
        sourceStream.writeline('}')
        sourceStream.writeline()
        sourceStream.writeline('void')
        sourceStream.writeline('{name}::out(std::ostream& os) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  os << "{name} " << *this;'.format(name = self.getName()))
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('void')
        sourceStream.writeline('{name}::dispatch(const AbstractMessageDispatcher& dispatcher) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  dispatcher.accept(*this);')
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('bool')
        sourceStream.writeline('{name}::operator==(const AbstractMessage& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        sourceStream.writeline('  const {name}* message = dynamic_cast<const {name}*>(&rhs);'.format(name = self.getName()))
        sourceStream.writeline('  if (!message)')
        sourceStream.writeline('    return false;')
        sourceStream.writeline('  return operator==(*message);')
        sourceStream.writeline('}')
        sourceStream.writeline()
        sourceStream.writeline('bool')
        sourceStream.writeline('{name}::operator==(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        for field in self.getFieldList():
            upperName = field.getUpperName()
            sourceStream.writeline('  if (get{upperName}() != rhs.get{upperName}()) return false;'.format(upperName = upperName))
        sourceStream.writeline('  return true;')
        sourceStream.writeline('}')
        sourceStream.writeline()
        sourceStream.writeline('bool')
        sourceStream.writeline('{name}::operator<(const {name}& rhs) const'.format(name = self.getName()))
        sourceStream.writeline('{')
        for field in self.getFieldList():
            upperName = field.getUpperName()
            sourceStream.writeline('  if (get{upperName}() < rhs.get{upperName}()) return true;'.format(upperName = upperName))
            sourceStream.writeline('  if (rhs.get{upperName}() < get{upperName}()) return false;'.format(upperName = upperName))
        sourceStream.writeline('  return false;')
        sourceStream.writeline('}')

        if self.getReliableExpression():
            sourceStream.writeline()
            sourceStream.writeline('bool')
            sourceStream.writeline('{name}::getReliable() const'.format(name = self.getName()))
            sourceStream.writeline('{')
            sourceStream.writeline('  return {expression};'.format(expression = self.getReliableExpression()))
            sourceStream.writeline('}')

        if self.getObjectInstanceExpression():
            sourceStream.writeline()
            sourceStream.writeline('ObjectInstanceHandle')
            sourceStream.writeline('{name}::getObjectInstanceHandleForMessage() const'.format(name = self.getName()))
            sourceStream.writeline('{')
            sourceStream.writeline('  return {expression};'.format(expression = self.getObjectInstanceExpression()))
            sourceStream.writeline('}')

        sourceStream.writeline()


###############################################################################
class MessageEncoding(object):
    def __init__(self, name):
        self.__name = name
        # Currently this is common, but as the first incompatible change starts
        # override this in the encodings
        self.__opcodeMap = {
            'ConnectionLostMessage' : 1,
            'CreateFederationExecutionRequestMessage' : 2,
            'CreateFederationExecutionResponseMessage' : 3,
            'DestroyFederationExecutionRequestMessage' : 4,
            'DestroyFederationExecutionResponseMessage' : 5,
            'EnumerateFederationExecutionsRequestMessage' : 6,
            'EnumerateFederationExecutionsResponseMessage' : 7,
            'InsertFederationExecutionMessage' : 8,
            'ShutdownFederationExecutionMessage' : 9,
            'EraseFederationExecutionMessage' : 10,
            'ReleaseFederationHandleMessage' : 11,
            'InsertModulesMessage' : 12,
            'JoinFederationExecutionRequestMessage' : 13,
            'JoinFederationExecutionResponseMessage' : 14,
            'ResignFederationExecutionRequestMessage' : 15,
            'JoinFederateNotifyMessage' : 16,
            'ResignFederateNotifyMessage' : 17,
            'ChangeAutomaticResignDirectiveMessage' : 18,
            'RegisterFederationSynchronizationPointMessage' : 30,
            'RegisterFederationSynchronizationPointResponseMessage' : 31,
            'AnnounceSynchronizationPointMessage' : 32,
            'SynchronizationPointAchievedMessage' : 33,
            'FederationSynchronizedMessage' : 34,
            'EnableTimeRegulationRequestMessage' : 40,
            'EnableTimeRegulationResponseMessage' : 41,
            'DisableTimeRegulationRequestMessage' : 42,
            'CommitLowerBoundTimeStampMessage' : 43,
            'CommitLowerBoundTimeStampResponseMessage' : 44,
            'LockedByNextMessageRequestMessage' : 45,
            'InsertRegionMessage' : 46,
            'CommitRegionMessage' : 47,
            'EraseRegionMessage' : 48,
            'ChangeInteractionClassPublicationMessage' : 50,
            'ChangeObjectClassPublicationMessage' : 51,
            'ChangeInteractionClassSubscriptionMessage' : 52,
            'ChangeObjectClassSubscriptionMessage' : 53,
            'ObjectInstanceHandlesRequestMessage' : 60,
            'ObjectInstanceHandlesResponseMessage' : 61,
            'ReleaseMultipleObjectInstanceNameHandlePairsMessage' : 62,
            'ReserveObjectInstanceNameRequestMessage' : 63,
            'ReserveObjectInstanceNameResponseMessage' : 64,
            'ReserveMultipleObjectInstanceNameRequestMessage' : 65,
            'ReserveMultipleObjectInstanceNameResponseMessage' : 66,
            'InteractionMessage' : 80,
            'TimeStampedInteractionMessage' : 81,
            'InsertObjectInstanceMessage' : 90,
            'DeleteObjectInstanceMessage' : 91,
            'TimeStampedDeleteObjectInstanceMessage' : 92,
            # obsolete 'LocalDeleteObjectInstanceMessage' : 93,
            'DestroyObjectInstanceMessage' : 94,
            'AttributeUpdateMessage' : 94,
            'TimeStampedAttributeUpdateMessage' : 96,
            'RequestAttributeUpdateMessage' : 97,
            'RequestClassAttributeUpdateMessage' : 98
        }

    def getName(self):
        return self.__name

    def getMessageOpcode(self, messageName):
        if messageName in self.__opcodeMap:
            return self.__opcodeMap[messageName]
        else:
            return None

    def writeCEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        typeName = dataType.getTypeName()
        encoding = dataType.getEncoding()
        sourceStream.writeline('void write{name}(const {ctype}& value)'.format(name = name, ctype = typeName))
        sourceStream.writeline('{')
        if typeName == 'VariableLengthData':
            sourceStream.writeline('  writeSizeTCompressed(value.size());')
            sourceStream.writeline('  if (!value.empty())')
            sourceStream.writeline('    _messageEncoding.addWriteBuffer(value);')
        elif typeName == 'std::string':
            sourceStream.writeline('  writeSizeTCompressed(value.size());')
            sourceStream.writeline('  for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {')
            sourceStream.writeline('    writeChar(*i);')
            sourceStream.writeline('  }')
        else:
            sourceStream.writeline('  write{encoding}Compressed(value);'.format(encoding = encoding))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeCDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        typeName = dataType.getTypeName()
        encoding = dataType.getEncoding()
        sourceStream.writeline('void read{name}({ctype}& value)'.format(name = name, ctype = typeName))
        sourceStream.writeline('{')
        if typeName == 'VariableLengthData':
            sourceStream.writeline('  size_t size = readSizeTCompressed();')
            sourceStream.writeline('  value.resize(size);')
            sourceStream.writeline('  if (size)')
            sourceStream.writeline('    _messageEncoding.addReadBuffer(size);')
        elif typeName == 'std::string':
            sourceStream.writeline('  value.resize(readSizeTCompressed());')
            sourceStream.writeline('  for (std::string::iterator i = value.begin(); i != value.end(); ++i) {')
            sourceStream.writeline('    *i = readChar();')
            sourceStream.writeline('  }')
        else:
            sourceStream.writeline('  value = read{encoding}Compressed();'.format(encoding = encoding))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeCPayloadDecoder(self, dataType, sourceStream):
        if not dataType.hasPayload():
            return
        name = dataType.getName()
        typeName = dataType.getTypeName()
        encoding = dataType.getEncoding()
        sourceStream.writeline('void readPayload{name}({ctype}& value)'.format(name = name, ctype = typeName))
        sourceStream.writeline('{')
        sourceStream.writeline('  if (!value.size())')
        sourceStream.writeline('    return;')
        sourceStream.writeline('  value = *_i;')
        sourceStream.writeline('  ++_i;')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeEnumEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        sourceStream.writeline('void write{name}(const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  switch (value) {')
        value = 0
        for enum in dataType.getEnumList():
            sourceStream.writeline('  case {enum}:'.format(enum = enum.getName()))
            sourceStream.writeline('    writeUInt32Compressed({value});'.format(value = value))
            sourceStream.writeline('    break;')
            value = value + 1
        sourceStream.writeline('  default:')
        sourceStream.writeline('    writeUInt32Compressed({value});'.format(value = value))
        sourceStream.writeline('    break;')
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeEnumDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        sourceStream.writeline('void read{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  switch (readUInt32Compressed()) {')
        value = 0
        for enum in dataType.getEnumList():
            sourceStream.writeline('  case {value}:'.format(value = value))
            sourceStream.writeline('    value = {enum};'.format(enum = enum.getName()))
            sourceStream.writeline('    break;')
            value = value + 1
        sourceStream.writeline('  default:')
        sourceStream.writeline('    value = {enum};'.format(enum = enum.getName()))
        sourceStream.writeline('    break;')
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeEnumPayloadDecoder(self, dataType, sourceStream):
        pass

    def writeVectorEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        scalarName = dataType.getScalarTypeName()
        sourceStream.writeline('void write{name}(const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  writeSizeTCompressed(value.size());')
        sourceStream.writeline('  for ({name}::const_iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        sourceStream.writeline('    write{scalarName}(*i);'.format(scalarName = scalarName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeVectorDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        scalarName = dataType.getScalarTypeName()
        sourceStream.writeline('void read{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  value.resize(readSizeTCompressed());')
        sourceStream.writeline('  for ({name}::iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        sourceStream.writeline('    read{scalarName}(*i);'.format(scalarName = scalarName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeVectorPayloadDecoder(self, dataType, sourceStream):
        if not dataType.hasPayload():
            return
        name = dataType.getName()
        scalarName = dataType.getScalarTypeName()
        sourceStream.writeline('void readPayload{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  for ({name}::iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        sourceStream.writeline('    readPayload{scalarName}(*i);'.format(scalarName = scalarName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()


    def writeSetEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        scalarName = dataType.getScalarTypeName()
        sourceStream.writeline('void write{name}(const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  writeSizeTCompressed(value.size());')
        sourceStream.writeline('  for ({name}::const_iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        sourceStream.writeline('    write{scalarName}(*i);'.format(scalarName = scalarName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeSetDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        scalarName = dataType.getScalarTypeName()
        sourceStream.writeline('void read{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  size_t size = readSizeTCompressed();')
        sourceStream.writeline('  for (; size != 0; --size) {')
        sourceStream.writeline('    {scalarName} scalar;'.format(scalarName = scalarName))
        sourceStream.writeline('    read{scalarName}(scalar);'.format(scalarName = scalarName))
        sourceStream.writeline('    value.insert(scalar);')
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeSetPayloadDecoder(self, dataType, sourceStream):
        if not dataType.hasPayload():
            return
        name = dataType.getName()
        scalarName = dataType.getScalarTypeName()
        sourceStream.writeline('void readPayload{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  for ({name}::iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        sourceStream.writeline('    readPayload{scalarName}(*i);'.format(scalarName = scalarName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()


    def writeMapEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        keyTypeName = dataType.getKeyTypeName()
        valueTypeName = dataType.getValueTypeName()
        sourceStream.writeline('void write{name}(const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  writeSizeTCompressed(value.size());')
        sourceStream.writeline('  for ({name}::const_iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        sourceStream.writeline('    write{keyTypeName}(i->first);'.format(keyTypeName = keyTypeName))
        sourceStream.writeline('    write{valueTypeName}(i->second);'.format(valueTypeName = valueTypeName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeMapDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        keyTypeName = dataType.getKeyTypeName()
        valueTypeName = dataType.getValueTypeName()
        sourceStream.writeline('void read{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  size_t size = readSizeTCompressed();')
        sourceStream.writeline('  for (; size != 0; --size) {')
        sourceStream.writeline('    {keyTypeName} key;'.format(keyTypeName = keyTypeName))
        sourceStream.writeline('    read{keyTypeName}(key);'.format(keyTypeName = keyTypeName))
        sourceStream.writeline('    read{valueTypeName}(value[key]);'.format(valueTypeName = valueTypeName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeMapPayloadDecoder(self, dataType, sourceStream):
        if not dataType.hasPayload():
            return
        name = dataType.getName()
        keyTypeName = dataType.getKeyTypeName()
        valueTypeName = dataType.getValueTypeName()
        sourceStream.writeline('void readPayload{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  for ({name}::iterator i = value.begin(); i != value.end(); ++i) {{'.format(name = name))
        if dataType.keyHasPayload():
            sourceStream.writeline('    readPayload{keyTypeName}(i->first);'.format(keyTypeName = keyTypeName))
        if dataType.valueHasPayload():
            sourceStream.writeline('    readPayload{valueTypeName}(i->second);'.format(valueTypeName = valueTypeName))
        sourceStream.writeline('  }')
        sourceStream.writeline('}')
        sourceStream.writeline()


    def writePairEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        firstTypeName = dataType.getFirstTypeName()
        secondTypeName = dataType.getSecondTypeName()
        sourceStream.writeline('void write{name}(const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  write{firstTypeName}(value.first);'.format(firstTypeName = firstTypeName))
        sourceStream.writeline('  write{secondTypeName}(value.second);'.format(secondTypeName = secondTypeName))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writePairDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        firstTypeName = dataType.getFirstTypeName()
        secondTypeName = dataType.getSecondTypeName()
        sourceStream.writeline('void read{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        sourceStream.writeline('  read{firstTypeName}(value.first);'.format(firstTypeName = firstTypeName))
        sourceStream.writeline('  read{secondTypeName}(value.second);'.format(secondTypeName = secondTypeName))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writePairPayloadDecoder(self, dataType, sourceStream):
        if not dataType.hasPayload():
            return
        name = dataType.getName()
        firstTypeName = dataType.getFirstTypeName()
        secondTypeName = dataType.getSecondTypeName()
        sourceStream.writeline('void readPayload{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        if dataType.firstHasPayload():
            sourceStream.writeline('  readPayload{firstTypeName}(value.first);'.format(firstTypeName = firstTypeName))
        if dataType.secondHasPayload():
            sourceStream.writeline('  readPayload{secondTypeName}(value.second);'.format(secondTypeName = secondTypeName))
        sourceStream.writeline('}')
        sourceStream.writeline()


    def writeStructEncoder(self, dataType, sourceStream):
        name = dataType.getName()
        sourceStream.writeline('void write{name}(const {name}& value)'.format(name = name))
        sourceStream.writeline('{')
        for field in dataType.getFieldList():
            fieldName = field.getName()
            typeName = field.getTypeName()
            sourceStream.writeline('  write{typeName}(value.get{fieldName}());'.format(typeName = typeName, fieldName = fieldName))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeStructDecoder(self, dataType, sourceStream):
        name = dataType.getName()
        sourceStream.writeline('void read{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        for field in dataType.getFieldList():
            fieldName = field.getName()
            typeName = field.getTypeName()
            sourceStream.writeline('  read{typeName}(value.get{fieldName}());'.format(typeName = typeName, fieldName = fieldName))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeStructPayloadDecoder(self, dataType, sourceStream):
        if not dataType.hasPayload():
            return
        name = dataType.getName()
        sourceStream.writeline('void readPayload{name}({name}& value)'.format(name = name))
        sourceStream.writeline('{')
        for field in dataType.getFieldList():
            if field.hasPayload():
                fieldName = field.getName()
                typeName = field.getTypeName()
                sourceStream.writeline('  readPayload{typeName}(value.get{fieldName}());'.format(typeName = typeName, fieldName = fieldName))
        sourceStream.writeline('}')
        sourceStream.writeline()

    def writeEncodingDeclaration(self, messageMap, sourceStream):
        encodingName = self.getName()
        sourceStream.writeCopyright()
        sourceStream.writeline('#ifndef OpenRTI_' + encodingName + 'MessageEncoding_h')
        sourceStream.writeline('#define OpenRTI_' + encodingName + 'MessageEncoding_h')
        sourceStream.writeline()
        sourceStream.writeline('#include "AbstractMessageEncoding.h"')
        sourceStream.writeline('#include "Export.h"')
        sourceStream.writeline()
        sourceStream.writeline('namespace OpenRTI {')
        sourceStream.writeline()

        sourceStream.writeline('class OPENRTI_LOCAL ' + encodingName + 'MessageEncoding : public AbstractMessageEncoding {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline(encodingName + 'MessageEncoding();')
        sourceStream.writeline('virtual ~' + encodingName + 'MessageEncoding();')
        sourceStream.writeline()

        sourceStream.writeline('virtual const char* getName() const;')
        sourceStream.writeline()

        sourceStream.writeline('virtual void readPacket(const Buffer& buffer);')
        sourceStream.writeline('void decodeBody(const VariableLengthData& variableLengthData);')
        sourceStream.writeline('void decodePayload(const Buffer::const_iterator& i);')
        sourceStream.writeline('virtual void writeMessage(const AbstractMessage& message);')
        sourceStream.writeline()

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()

        sourceStream.writeline('class DecodeStream;')
        sourceStream.writeline('class DispatchFunctor;')
        sourceStream.writeline('class PayloadDecoder;')
        sourceStream.writeline('class EncodeStream;')
        sourceStream.writeline()
        sourceStream.writeline('SharedPtr<AbstractMessage> _message;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()
        sourceStream.writeline('} // namespace OpenRTI')
        sourceStream.writeline()
        sourceStream.writeline('#endif')

    def writeEncodingImplementation(self, messageMap, sourceStream):
        encodingName = self.getName()
        encodingClass = encodingName + 'MessageEncoding'
        sourceStream.writeCopyright()
        sourceStream.writeline()
        sourceStream.writeline('#include "' + encodingName + 'MessageEncoding.h"')
        sourceStream.writeline('#include "AbstractMessageEncoding.h"')
        sourceStream.writeline('#include "DecodeDataStream.h"')
        sourceStream.writeline('#include "EncodeDataStream.h"')
        sourceStream.writeline('#include "Export.h"')
        sourceStream.writeline('#include "Message.h"')
        sourceStream.writeline()
        sourceStream.writeline('namespace OpenRTI {')
        sourceStream.writeline()

        sourceStream.writeline('class OPENRTI_LOCAL ' + encodingClass + '::EncodeStream : public EncodeDataStream {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('EncodeStream(VariableLengthData& variableLengthData, ' + encodingClass + '& messageEncoding) :')
        sourceStream.writeline('  EncodeDataStream(variableLengthData),')
        sourceStream.writeline('  _messageEncoding(messageEncoding)')
        sourceStream.writeline('{ }')

        for t in messageMap.getTypeList():
            t.writeComponent('Encoder', sourceStream, self)

        sourceStream.writeline(encodingName + 'MessageEncoding& _messageEncoding;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()


        sourceStream.writeline('class OPENRTI_LOCAL ' + encodingClass + '::DispatchFunctor {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('DispatchFunctor(' + encodingClass + '& messageEncoding) :')
        sourceStream.writeline('  _messageEncoding(messageEncoding)')
        sourceStream.writeline('{ }')
        sourceStream.writeline()

        sourceStream.writeline('template<typename M>')
        sourceStream.writeline('void operator()(const M& message) const')
        sourceStream.writeline('{ encode(_messageEncoding, message); }')
        sourceStream.writeline()

        sourceStream.writeline('void')
        sourceStream.writeline('encode(' + encodingClass + '&, const AbstractMessage&) const')
        sourceStream.writeline('{')
        sourceStream.writeline('  throw RTIinternalError("Invalid message dispatched to encoding!");')
        sourceStream.writeline('}')
        sourceStream.writeline()

        for t in messageMap.getTypeList():
            messageName = t.getName()
            opcode = self.getMessageOpcode(messageName)
            if opcode is None:
                continue
            sourceStream.writeline('void')
            sourceStream.writeline('encode(' + encodingClass + '& messageEncoding, const {messageName}& message) const'.format(messageName = messageName))
            sourceStream.writeline('{')
            sourceStream.pushIndent()
            sourceStream.writeline('EncodeDataStream headerStream(messageEncoding.addScratchWriteBuffer());')
            sourceStream.writeline('EncodeStream encodeStream(messageEncoding.addScratchWriteBuffer(), messageEncoding);')
            sourceStream.writeline('encodeStream.writeUInt16Compressed({opcode});'.format(opcode = opcode))
            sourceStream.writeline('encodeStream.write{messageName}(message);'.format(messageName = messageName))
            sourceStream.writeline('headerStream.writeUInt32BE(uint32_t(encodeStream.size()));')
            sourceStream.popIndent()
            sourceStream.writeline('}')
            sourceStream.writeline()

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()
        sourceStream.writeline(encodingClass + '& _messageEncoding;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()


        sourceStream.writeline('class OPENRTI_LOCAL ' + encodingClass + '::DecodeStream : public DecodeDataStream {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('DecodeStream(const VariableLengthData& variableLengthData, ' + encodingClass + '& messageEncoding) :')
        sourceStream.writeline('  DecodeDataStream(variableLengthData),')
        sourceStream.writeline('  _messageEncoding(messageEncoding)')
        sourceStream.writeline('{ }')

        for t in messageMap.getTypeList():
            t.writeComponent('Decoder', sourceStream, self)

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()
        sourceStream.writeline(encodingClass + '& _messageEncoding;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()


        sourceStream.writeline('class OPENRTI_LOCAL ' + encodingClass + '::PayloadDecoder {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('PayloadDecoder(const Buffer::const_iterator& i) :')
        sourceStream.writeline('  _i(i)')
        sourceStream.writeline('{ }')

        for t in messageMap.getTypeList():
            t.writeComponent('PayloadDecoder', sourceStream, self)

        sourceStream.writeline('Buffer::const_iterator _i;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()


        sourceStream.writeline(encodingName + 'MessageEncoding::' + encodingName + 'MessageEncoding()')
        sourceStream.writeline('{')
        sourceStream.writeline('}')
        sourceStream.writeline()
        sourceStream.writeline(encodingName + 'MessageEncoding::~' + encodingName + 'MessageEncoding()')
        sourceStream.writeline('{')
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('const char*')
        sourceStream.writeline(encodingName + 'MessageEncoding::getName() const')
        sourceStream.writeline('{')
        sourceStream.writeline('  return "' + encodingName + '";')
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('void')
        sourceStream.writeline(encodingName + 'MessageEncoding::readPacket(const Buffer& buffer)')
        sourceStream.writeline('{')
        sourceStream.pushIndent()
        sourceStream.writeline('Buffer::const_iterator i = buffer.begin();')
        sourceStream.writeline('if (i == buffer.end()) {')
        sourceStream.writeline('  addScratchReadBuffer(4);')
        sourceStream.writeline('} else if (++i == buffer.end()) {')
        sourceStream.writeline('  addScratchReadBuffer(buffer.front().getUInt32BE(0));')
        sourceStream.writeline('} else if (++i == buffer.end()) {')
        sourceStream.writeline('  decodeBody(*(--i));')
        sourceStream.writeline('} else {')
        sourceStream.writeline('  decodePayload(i);')
        sourceStream.writeline('}')
        sourceStream.writeline('if (getInputBufferComplete())')
        sourceStream.writeline('  getConnect()->send(SharedPtr<AbstractMessage>().swap(_message));')
        sourceStream.popIndent()
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('void')
        sourceStream.writeline(encodingClass + '::decodeBody(const VariableLengthData& variableLengthData)')
        sourceStream.writeline('{')
        sourceStream.pushIndent()
        sourceStream.writeline('DecodeStream decodeStream(variableLengthData, *this);')
        sourceStream.writeline('uint16_t opcode = decodeStream.readUInt16Compressed();')
        sourceStream.writeline('switch (opcode) {')

        for t in messageMap.getTypeList():
            messageName = t.getName()
            opcode = self.getMessageOpcode(messageName)
            if opcode is None:
                continue
            sourceStream.writeline('case {opcode}:'.format(opcode = opcode))
            sourceStream.pushIndent()
            sourceStream.writeline('_message = new {messageName};'.format(messageName = messageName))
            sourceStream.writeline('decodeStream.read{messageName}(static_cast<{messageName}&>(*_message));'.format(messageName = messageName))
            sourceStream.writeline('break;')
            sourceStream.popIndent()

        sourceStream.writeline('default:')
        sourceStream.writeline('  break;')
        sourceStream.writeline('}')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

        sourceStream.writeline('void')
        sourceStream.writeline(encodingClass + '::decodePayload(const Buffer::const_iterator& i)')
        sourceStream.writeline('{')
        sourceStream.pushIndent()
        sourceStream.writeline('Buffer::const_iterator j = i;')
        sourceStream.writeline('DecodeDataStream decodeStream(*(--j));')
        sourceStream.writeline('uint16_t opcode = decodeStream.readUInt16Compressed();')
        sourceStream.writeline('PayloadDecoder payloadDecoder(i);')
        sourceStream.writeline('switch (opcode) {')
        for t in messageMap.getTypeList():
            messageName = t.getName()
            opcode = self.getMessageOpcode(messageName)
            if opcode is None:
                continue
            if not t.hasPayload():
                continue
            sourceStream.writeline('case {opcode}:'.format(opcode = opcode))
            sourceStream.pushIndent()
            sourceStream.writeline('payloadDecoder.readPayload{messageName}(static_cast<{messageName}&>(*_message));'.format(messageName = messageName))
            sourceStream.writeline('break;')
            sourceStream.popIndent()
        sourceStream.writeline('default:')
        sourceStream.writeline('  break;')
        sourceStream.writeline('}')
        sourceStream.popIndent()
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('void')
        sourceStream.writeline(encodingName + 'MessageEncoding::writeMessage(const AbstractMessage& message)')
        sourceStream.writeline('{')
        sourceStream.writeline('  message.dispatchFunctor(DispatchFunctor(*this));')
        sourceStream.writeline('}')
        sourceStream.writeline()

        sourceStream.writeline('} // namespace OpenRTI')
        sourceStream.writeline()

###############################################################################
# FIXME move actual value encodings to here

class TightBE1MessageEncoding(MessageEncoding):
    def __init__(self):
        MessageEncoding.__init__(self, 'TightBE1')

# class AlignedLE1MessageEncoding(MessageEncoding):
#     def __init__(self):
#         MessageEncoding.__init__(self, 'AlignedLE1')

# class AlignedBE1MessageEncoding(MessageEncoding):
#     def __init__(self):
#         MessageEncoding.__init__(self, 'AlignedBE1')


###############################################################################
class TypeMap(object):
    def __init__(self, node):
        self.__typeList = []
        self.__typeMap = {}

        while node:
            if node.type == 'element':
                if node.name == 'message':
                    message = MessageDataType(node.prop('type') + 'Message', 'AbstractMessage')
                    field = node.children
                    while field:
                        if field.type == 'element' and field.name == 'field':
                            message.addField(field.prop('name'), field.prop('type'))
                        elif field.type == 'element' and field.name == 'reliable':
                            message.setReliableExpression(field.prop('expression'))
                        elif field.type == 'element' and field.name == 'objectInstance':
                            message.setObjectInstanceExpression(field.prop('expression'))
                        field = field.next
                    self.addType(message)

                elif node.name == 'type':
                    typeName = node.prop('type')
                    if typeName == 'enum':
                        enum = EnumDataType(node.prop('name'))
                        enumerant = node.children
                        while enumerant:
                            if enumerant.type == 'element' and enumerant.name == 'enumerant':
                                enum.addEnum(enumerant.prop('name'), enumerant.prop('value'))
                            enumerant = enumerant.next
                        self.addType(enum)

                    elif typeName == 'vector':
                        self.addType(VectorDataType(node.prop('name'), node.prop('scalar')))

                    elif typeName == 'set':
                        self.addType(SetDataType(node.prop('name'), node.prop('scalar')))

                    elif typeName == 'map':
                        self.addType(MapDataType(node.prop('name'), node.prop('key'), node.prop('value')))

                    elif typeName == 'pair':
                        self.addType(PairDataType(node.prop('name'), node.prop('first'), node.prop('second')))

                    elif typeName == 'struct':
                        struct = StructDataType(node.prop('name'))
                        struct.setCopyOnWrite(node.prop('copyOnWrite') == 'true')
                        field = node.children
                        while field:
                            if field.type == 'element' and field.name == 'field':
                                struct.addField(field.prop('name'), field.prop('type'))
                            field = field.next
                        self.addType(struct)
                    else:
                        dataType = CDataType(node.prop('name'), node.prop('encoding'), node.prop('ctype'))
                        self.addType(dataType)
            node = node.next

        for t in self.__typeList:
            t.resolveHasPayloadAndHasSwap(self)

    def addType(self, t):
        self.__typeList.append(t)
        self.__typeMap[t.getName()] = t

    def getTypeList(self):
        return self.__typeList

    def getType(self, name):
        return self.__typeMap[name]

    def writeDeclaration(self, sourceStream):
        sourceStream.writeCopyright()
        sourceStream.writeline('#ifndef OpenRTI_Message_h')
        sourceStream.writeline('#define OpenRTI_Message_h')
        sourceStream.writeline()
        sourceStream.writeline('#include <map>')
        sourceStream.writeline('#include <set>')
        sourceStream.writeline('#include <vector>')
        sourceStream.writeline('#include "AbstractMessage.h"')
        sourceStream.writeline('#include "AbstractMessageDispatcher.h"')
        sourceStream.writeline('#include "Handle.h"')
        sourceStream.writeline('#include "VariableLengthData.h"')
        sourceStream.writeline()
        sourceStream.writeline('namespace OpenRTI {')
        sourceStream.writeline()
        for t in self.__typeList:
            t.writeForwardDeclaration(sourceStream)
        sourceStream.writeline()
        for t in self.__typeList:
            t.writeDeclaration(sourceStream)
        sourceStream.writeline()
        for t in self.__typeList:
            t.writeStreamOut(sourceStream)
        sourceStream.writeline('} // namespace OpenRTI')
        sourceStream.writeline()
        sourceStream.writeline('#endif')

    def writeImplementation(self, sourceStream):
        sourceStream.writeCopyright()
        sourceStream.writeline()
        sourceStream.writeline('#include "Message.h"')
        sourceStream.writeline()
        sourceStream.writeline('#include <ostream>')
        sourceStream.writeline('#include "AbstractMessage.h"')
        sourceStream.writeline('#include "AbstractMessageDispatcher.h"')
        sourceStream.writeline('#include "StringUtils.h"')
        sourceStream.writeline()
        sourceStream.writeline('namespace OpenRTI {')
        sourceStream.writeline()
        for t in self.__typeList:
            t.writeImplementation(sourceStream)
        sourceStream.writeline('} // namespace OpenRTI')

    def writeDispatcher(self, sourceStream):
        sourceStream.writeCopyright()
        sourceStream.writeline('#ifndef OpenRTI_AbstractMessageDispatcher_h')
        sourceStream.writeline('#define OpenRTI_AbstractMessageDispatcher_h')
        sourceStream.writeline()
        sourceStream.writeline('namespace OpenRTI {')
        sourceStream.writeline()
        sourceStream.writeline('class AbstractMessage;')
        sourceStream.writeline()

        for t in self.__typeList:
            if not t.isMessage():
                continue
            t.writeForwardDeclaration(sourceStream)

        sourceStream.writeline()

        sourceStream.writeline('class OPENRTI_LOCAL AbstractMessageDispatcher {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('virtual ~AbstractMessageDispatcher() {}')
        sourceStream.writeline()

        for t in self.__typeList:
            if not t.isMessage():
                continue
            sourceStream.writeline('virtual void accept(const {name}&) const = 0;'.format(name = t.getName()))

        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

        sourceStream.writeline('template<typename T>')
        sourceStream.writeline('class OPENRTI_LOCAL FunctorMessageDispatcher : public AbstractMessageDispatcher {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('FunctorMessageDispatcher(T& t) : _t(t) {}')
        sourceStream.writeline('virtual ~FunctorMessageDispatcher() {}')
        sourceStream.writeline()

        for t in self.__typeList:
            if not t.isMessage():
                continue
            sourceStream.writeline('virtual void accept(const {name}& message) const {{ _t(message); }}'.format(name = t.getName()))

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()
        sourceStream.writeline('T& _t;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

        sourceStream.writeline('template<typename T>')
        sourceStream.writeline('class OPENRTI_LOCAL ConstFunctorMessageDispatcher : public AbstractMessageDispatcher {')
        sourceStream.writeline('public:')
        sourceStream.pushIndent()
        sourceStream.writeline('ConstFunctorMessageDispatcher(const T& t) : _t(t) {}')
        sourceStream.writeline('virtual ~ConstFunctorMessageDispatcher() {}')
        sourceStream.writeline()

        for t in self.__typeList:
            if not t.isMessage():
                continue
            sourceStream.writeline('virtual void accept(const {name}& message) const {{ _t(message); }}'.format(name = t.getName()))

        sourceStream.popIndent()
        sourceStream.writeline('private:')
        sourceStream.pushIndent()
        sourceStream.writeline('const T& _t;')
        sourceStream.popIndent()
        sourceStream.writeline('};')
        sourceStream.writeline()

        sourceStream.writeline('} // namespace OpenRTI')
        sourceStream.writeline()
        sourceStream.writeline('#endif')

    def writeEncodingDeclaration(self, sourceStream, messageEncoding):
        messageEncoding.writeEncodingDeclaration(self, sourceStream)

    def writeEncodingImplementation(self, sourceStream, messageEncoding):
        messageEncoding.writeEncodingImplementation(self, sourceStream)


# The main application

import sys
import getopt
import libxml2

messageDefinitionFile = 'codegen/Message.xml'
outputMode = ''

# try:
(args, trail) = getopt.getopt(sys.argv[1:], 'm:O:')
# except Exception,e:
#     show_usage()

for (arg, val) in args:
    if arg == '-m':
        messageDefinitionFile = val
    elif arg == '-O':
        outputMode = val

# read the xml document describing the types
#doc = libxml2.readFile(messageDefinitionFile, None,
#    libxml2.XML_PARSE_DTDLOAD +
#    libxml2.XML_PARSE_DTDVALID +
#    libxml2.XML_PARSE_NOBLANKS)
doc = libxml2.readFile(messageDefinitionFile, None, libxml2.XML_PARSE_NOBLANKS)
rootElement = doc.getRootElement()
typeMap = TypeMap(rootElement.children)
doc.freeDoc()

# These are the output modes for debugging specific stages
if outputMode == 'MessageDeclaration':
    typeMap.writeDeclaration(SourceStream(sys.stdout))
elif outputMode == 'MessageImplementation':
    typeMap.writeImplementation(SourceStream(sys.stdout))
elif outputMode == 'MessageDispatcher':
    typeMap.writeDispatcher(SourceStream(sys.stdout))
elif outputMode == 'MessageEncodingDeclaration':
    messageEncoding = TightBE1MessageEncoding()
    typeMap.writeEncodingDeclaration(SourceStream(SourceStream(sys.stdout)), messageEncoding)
elif outputMode == 'MessageEncodingImplementation':
    messageEncoding = TightBE1MessageEncoding()
    typeMap.writeEncodingImplementation(SourceStream(SourceStream(sys.stdout)), messageEncoding)
else:
    # This is the shortcut for 'just do all output into the current directory'
    typeMap.writeDeclaration(SourceStream(open('Message.h', 'w+')))
    typeMap.writeImplementation(SourceStream(open('Message.cpp', 'w+')))
    typeMap.writeDispatcher(SourceStream(open('AbstractMessageDispatcher.h', 'w+')))

    messageEncoding = TightBE1MessageEncoding()
    typeMap.writeEncodingDeclaration(SourceStream(open('TightBE1MessageEncoding.h', 'w+')), messageEncoding)
    typeMap.writeEncodingImplementation(SourceStream(open('TightBE1MessageEncoding.cpp', 'w+')), messageEncoding)
